
#ifndef _CUDA_BINDINGS_
#define _CUDA_BINDINGS_

#include <ttg/structures.h>
#include <ttg_ext/cuda_bindings.h>

#include "Common/InterleavedBodies.h"

using namespace ttg::structures;
using namespace ttg_ext::bindings;

//Positions and masses of bodies, that is stored on GPU.
//Will be used for acceleration (to avoid additional copying).
struct BodiesOnGPU
{
	private:
		//Creates copy of the required data in GPU memory.
		//Do not forget to free returned pointer!
		float *copyToGPU(float *data_host, size_t count);
		//Releases GPU-memory.
		void release(float *data_dev);
	public:
		float *pos_x;			//array of x-coordinates of bodies (on GPU).
		float *pos_y;			//array of y-coordinates of bodies (on GPU).
		float *pos_z;			//array of z-coordinates of bodies (on GPU).
		float *masses;			//array of masses of bodies (on GPU).
		size_t count;			//total count of bodies.
	public:
		//Copies positions and masses of bodies to GPU-memory.
		BodiesOnGPU(InterleavedBodies *bodies_host);
		//Releases all used GPU-memory.
		~BodiesOnGPU();
};

class cudaInteractor :public cudaBinding<InterleavedBodies::SubVector,
										 InterleavedBodies::SubVector>
{
	private:
		BodiesOnGPU *bodies_on_gpu;				//bodies, that is stored in GPU memory.
	private:
		//Interacts required range of bodies with others.
		void interactBodies(InterleavedBodies::SubVector bodies, size_t lo, size_t hi);
	public:
		inline cudaInteractor()
		{
			bodies_on_gpu = NULL;
		}
		virtual double getDifficulty(InterleavedBodies::SubVector data)
		{
			return data.getTag() == 42 ? 1 : -1;		//rejecting small parts, waiting for a big one.
		}
		virtual void process(InterleavedBodies::SubVector data)
		{
			try
			{
				if (bodies_on_gpu == NULL)
					bodies_on_gpu = new BodiesOnGPU(data.getOwner());
				interactBodies(data.getOwnerSubVector(),
							   data.getRealIndex(0),
							   data.getRealIndex(data.getSize()));
				sendNext(data);
			}
			catch (ttg::Error &err)
			{ printf("CUDA error: %s\n", err.toString()); }
		}
		virtual void onEnd()
		{
			try
			{ SAFE_DELETE(bodies_on_gpu); }
			catch (ttg::Error &err)
			{ printf("CUDA error: %s\n", err.toString()); }
		}
};

#endif
